package com.basker.pisces.core.query.meta;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.lang3.NotImplementedException;

import com.basker.pisces.core.meta.DataField;
import com.basker.pisces.core.meta.IDataField;
import com.basker.pisces.core.meta.IDataObjectMeta;
import com.basker.pisces.core.meta.IField;
import com.basker.pisces.core.meta.row.RowReferenceField;
import com.basker.pisces.core.query.OrderField;
import com.basker.pisces.core.query.QueryInfo;

public class ReferenceFieldDesc extends FieldDesc implements IReferenceFieldDesc {

    private transient IDataObjectMeta referenceObjectMeta;

    /**
     * 用于获取引用表上的字段元数据，select模式下可以通过字段别名获取对应的元数据字段
     */
    private transient Map<String/* referenced field alias */, IField /* field in referenceObjectMeta */> referencedFieldMetaMap;

    private TableDesc referenceTable;
    private FieldDesc referencedCodeField;
    private FieldDesc referencedNameField;

    private List<FieldDesc> otherReferencedFields = new ArrayList<>();

    public ReferenceFieldDesc() {

    }

    /**
     * 创建一个字段
     *
     * @param fieldId         id，在{@link QueryMeta}的所有字段中唯一
     * @param name            对应数据库的字段名
     * @param alias           查询语句中，该字段的映射名
     * @param displayName     显示名称
     * @param refDataContract 关联的对象契约
     */
    public ReferenceFieldDesc(String id, String name, String alias, String displayName,
                              IDataObjectMeta referenceObjectMeta) {
        super(id, name, alias, displayName, referenceObjectMeta.getPkField().getFieldClass());
        this.referenceObjectMeta = referenceObjectMeta;
    }

    public void addOtherReferencedField(FieldDesc fieldDesc) {
        this.otherReferencedFields.add(fieldDesc);
    }

    /**
     * 构建被引用的子集集合，使之与referenceObjectMeta的field可以对应，select模式下，可以找到引用字段对应的元数据字段，进而从缓存对象中获取值
     */
    public void buildReferencedFields() {
        // 初始化map
        int referencedFieldCount = 0;

        if (this.referencedCodeField != null) {
            referencedFieldCount++;
        }

        if (this.referencedNameField != null) {
            referencedFieldCount++;
        }

        referencedFieldCount += this.otherReferencedFields.size();
        if (referencedFieldCount == 0) {
            return;
        }

        this.referencedFieldMetaMap = new HashMap<>(referencedFieldCount, 1);

        // 构建映射
        this.buildReferencedField(this.referencedCodeField);
        this.buildReferencedField(this.referencedNameField);
        for (FieldDesc fieldDesc : otherReferencedFields) {
            this.buildReferencedField(fieldDesc);
        }

    }

    @Override
    public IField createField(QueryInfo queryInfo) {
        DataField refrenceIdField = (DataField) super.createField(queryInfo);

        if (queryInfo.isSelectMode()) {
            refrenceIdField.setName(this.getAlias() + "_" + IDataField.ID_FIELD_NAME);
            return new RowReferenceField(this, refrenceIdField);
        } else {
            return refrenceIdField;
        }
    }

    @Override
    public String getOrderFieldName(OrderField orderField) {
        if (this.referencedCodeField != null) {
            return this.referencedCodeField.getOrderFieldName(orderField);
        } else {
            return super.getOrderFieldName(orderField);
        }
    }

    @Override
    public List<FieldDesc> getOtherReferencedFields() {
        return otherReferencedFields;
    }

    @Override
    public FieldDesc getReferencedCodeField() {
        return this.referencedCodeField;
    }

    /**
     * 获取referencedFieldMetaMap的不可变副本
     *
     * @return
     */
    public Map<String, IField> getReferencedFieldMetaMap() {
        if (referencedFieldMetaMap == null) {
            return Collections.emptyMap();
        }

        return Collections.unmodifiableMap(referencedFieldMetaMap);
    }

    @Override
    public FieldDesc getReferencedNameField() {
        return referencedNameField;
    }

    /**
     * 关联对象的数据契约
     *
     * @return
     */
    public IDataObjectMeta getReferenceObjectMeta() {
        return referenceObjectMeta;
    }

    @Override
    public TableDesc getReferenceTable() {
        return referenceTable;
    }

    public void setReferencedCodeField(FieldDesc referencedCodeField) {
        this.referencedCodeField = referencedCodeField;
    }

    public void setReferencedNameField(FieldDesc referencedNameField) {
        this.referencedNameField = referencedNameField;
    }

    public void setReferenceObjectMeta(IDataObjectMeta referenceObjectMeta) {
        this.referenceObjectMeta = referenceObjectMeta;
    }

    /**
     * 设置当前字段的关联表，同时将其设置为关联表代码字段和名称字段的所属表
     *
     * @param refTable
     */
    public void setReferenceTable(TableDesc referenceTable) {
        this.referenceTable = referenceTable;
        referenceTable.setFkField(this);
    }

    private void buildReferencedField(FieldDesc fieldDesc) {
        if (fieldDesc == null) {
            return;
        }

        IField field = null;

        String hostTableName = fieldDesc.getHostTable().getName();
        String dbFieldName = fieldDesc.getName();

        if (hostTableName.equals(this.referenceObjectMeta.getTableName())) {
            // 说明fieldDesc可以在referenceObjectMeta的dataFields上找到
            for (IDataField dataField : this.referenceObjectMeta.getDataFields()) {
                if (dataField.getDbFieldName().equals(dbFieldName)) {
                    field = dataField;
                    break;
                }
            }

            if (field == null) {
                throw new IllegalArgumentException(
                        "fieldDesc:'" + fieldDesc.getAlias() + "' can not be found in referenceObjectMeta.");
            }
        }

        if (field != null) {
            this.referencedFieldMetaMap.put(fieldDesc.getAlias(), field);
        } else {
            // TODO 后续再处理关联表的关联表上的字段......
            throw new NotImplementedException("not Implemented!");
        }

    }

}
